package com.mikufufu.blog.utils;

import com.alibaba.fastjson2.JSON;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类，提供对Redis简单操作的方法封装。
 * <p>
 *     含{@code @SuppressWarnings}注解的代码段。value 该参数指定了要抑制的警告类型，可以是多个类型。
 *     例如，{@code "unchecked"}用于抑制未检查类型转换的警告，{@code "rawtypes"}用于抑制使用原始类型而不是泛型的警告。
 *     该注释的目的是解释为什么需要抑制这些特定的警告，以及在没有其他更好解决方法的情况下，这种做法的合理性。
 *     如果不希望抑制这些警告，可以使用其他方法来避免它们。
 * </p>
 * 
 */
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public class RedisUtils {

    // RedisTemplate实例，用于执行Redis操作。
    private final static RedisTemplate redisTemplate = (RedisTemplate) SpringUtils.getBean("redisTemplate");

    /**
     * 将给定的值设置到指定的键上。
     * @param key 键名
     * @param value 要设置的值
     */
    public static <T> void set(String key, T value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 将给定的值设置到指定的键上，并设定过期时间。
     * @param key 键名
     * @param value 要设置的值
     * @param time 过期时间, 单位为秒
     */
    public static <T> void set(String key, T value, long time) {
        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
    }

    /**
     * 将给定的值设置到指定的键上，并设定过期时间。
     * @param key 键名
     * @param value 要设置的值
     * @param time 过期时间 long
     * @param timeUnit 过期时间单位
     */
    public static <T> void set(String key, T value, long time, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, time, timeUnit);
    }

    /**
     * 获取指定键的值。
     * @param key 键名
     * @return 键对应的值
     */
    public static Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 获取指定键的值，并将其转换为指定类型。
     * @param key 键名
     * @param clazz 需要转换成的目标类型
     * @param <T> 目标类型
     * @return 键对应的值，转换后的类型为clazz指定的类型
     */
    public static <T> T get(String key, Class<T> clazz) {
        return StringUtils.convert(redisTemplate.opsForValue().get(key), clazz);
    }

    /**
     * 将指定列表值添加到Redis列表的末尾(右侧)。
     *
     * @param key Redis中列表的键名。
     * @param value 要添加到列表中的值，该值是一个泛型列表。
     * @return 添加成功后列表的长度，如果添加前列表不存在则返回0。
     */
    public static <T> long setList(String key, List<T> value) {
        // 使用RedisTemplate向指定key的列表末尾添加所有value的元素
        Long count = redisTemplate.opsForList().rightPushAll(key, value);
        // 如果count为null，表示添加前列表不存在，返回0；否则返回添加后的列表长度
        return count == null ? 0: count;
    }

    /**
     * 将指定列表值添加到Redis列表的末尾(右侧)。
     *
     * @param key Redis中列表的键名。
     * @param value 要添加到列表中的值，该值是一个泛型列表。
     * @param time 添加后过期时间，单位为秒。
     * @return 添加成功后列表的长度，如果添加前列表不存在则返回0。
     */
    public static <T> long setList(String key, List<T> value, Long time) {
        // 使用RedisTemplate向指定key的列表末尾添加所有value的元素
        Long count = redisTemplate.opsForList().rightPushAll(key, value);
        if (time != null && time > 0) {
            // 如果time大于0，则设置过期时间
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
        // 如果count为null，表示添加前列表不存在，返回0；否则返回添加后的列表长度
        return count == null ? 0: count;
    }

    /**
     * 将指定列表值添加到Redis列表的末尾(右侧)。
     *
     * @param key Redis中列表的键名。
     * @param value 要添加到列表中的值，该值是一个泛型列表。
     * @param time 添加后过期时间，单位为秒。
     * @param timeUnit 添加后过期时间单位。
     * @return 添加成功后列表的长度，如果添加前列表不存在则返回0。
     */
    public static <T> long setList(String key, List<T> value, Long time, TimeUnit timeUnit) {
        // 使用RedisTemplate向指定key的列表末尾添加所有value的元素
        Long count = redisTemplate.opsForList().rightPushAll(key, value);
        if (time != null && time > 0) {
            // 如果time大于0，则设置过期时间
            redisTemplate.expire(key, time, timeUnit);
        }
        // 如果count为null，表示添加前列表不存在，返回0；否则返回添加后的列表长度
        return count == null ? 0: count;
    }

    /**
     * 向Redis的列表类型数据中添加元素。
     *
     * @param key Redis中列表的键名。
     * @param value 要添加到列表中的元素。
     * @return 如果添加前列表不存在，返回0；否则返回添加后的列表长度。
     * @param <T> 列表中元素的类型。
     */
    public static <T> long addList(String key, T value) {
        // 向指定key的列表中添加value元素
        Long count = redisTemplate.opsForList().rightPush(key, value);
        // 判断添加结果，若为null表示列表之前不存在，返回0；否则返回添加的元素数量
        return count == null ? 0: count;
    }

    /**
     * 向Redis的Set类型数据中添加元素。
     *
     * @param key Redis中Set的键名。
     * @param value 要添加到Set中的元素集合。
     * @return 如果添加前Set不存在，返回0；否则返回添加后的Set长度。
     * @param <T> Set中元素的类型。
     */
    public static <T> long setSet(String key, Set<T> value) {
        // 向指定key的Set中添加所有value的元素
        Long count = redisTemplate.opsForSet().add(key, value.toArray());
        // 判断添加结果，若为null表示Set之前不存在，返回0；否则返回添加的元素数量
        return count == null ? 0: count;
    }

    public static <T> long addSet(String key, T value) {
        // 向指定key的Set中添加value元素
        Long count = redisTemplate.opsForSet().add(key, value);
        // 判断添加结果，若为null表示Set之前不存在，返回0；否则返回添加的元素数量
        return count == null ? 0: count;
    }

    /**
     * 从Redis列表中获取指定键的所有元素。
     *
     * @param key 列表的键名，对应Redis中的键。
     * @return 返回一个包含列表所有元素的List，类型为泛型T。
     */
    public static <T> List<T> getList(String key) {
        // 通过redisTemplate操作Redis列表，获取key对应列表的所有元素
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 根据给定的键从Redis中获取集合并返回。
     *
     * @param key 用于获取集合的Redis键。
     * @return 返回与给定键关联的集合。如果键不存在，返回空集合。
     * @param <T> 集合元素的类型。
     */
    public static <T> Set<T> getSet(String key) {
        // 从Redis中获取指定key的集合成员
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 删除指定键的值。
     * @param key 键名
     */
    public static Boolean delete(String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 批量删除Redis中的键值对。
     *
     * @param keys 要删除的键的集合，键的类型为String。
     * @return 返回一个布尔值，如果删除成功（即删除的键数量大于0），则返回true；否则返回false。
     */
    public static Boolean delete(Collection<String> keys) {
        // 从Redis中删除指定的键，并返回删除的键的数量
        Long count = redisTemplate.delete(keys);
        // 判断删除的键的数量是否大于0，若大于0则返回true，否则返回false
        return count > 0;
    }

    /**
     * 获取所有匹配给定模式的键名。
     *
     * @param pattern 键名模式，可以使用通配符，例如"*"表示所有键。
     * @return 匹配模式的键名列表。
     */
    public static List<String> keys(String pattern) {
        // 通过RedisTemplate获取所有匹配pattern的键名，转换为ArrayList并返回
        return new ArrayList<>(Objects.requireNonNull(redisTemplate.keys(pattern)));
    }

    /**
     * 获取所有键名。
     *
     * @return 所有键名的列表。
     */
    public static List<String> keys() {
        // 调用keys方法，传入"*"作为模式，以获取所有键名
        return keys("*");
    }

    /**
     * 在Redis中放入哈希表数据
     *
     * @param key 哈希表的键
     * @param hashKey 哈希表中的字段键
     * @param value 哈希表中的字段值
     * @param <T> 字段值的类型
     */
    public static <T> void putHash(String key, String hashKey, T value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * 在Redis中放入哈希表数据
     *
     * @param key 哈希表的键
     * @param map 包含多个字段键值对的映射
     * @param <T> 字段值的类型
     */
    public static <T> void putHash(String key, Map<String, T> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 从Redis中获取哈希表中的特定字段值
     *
     * @param key 哈希表的键
     * @param hashKey 哈希表中的字段键
     * @param <T> 字段值的类型
     * @return 字段值，如果不存在则返回null
     */
    public static <T> T getHash(String key, String hashKey) {
        return (T) redisTemplate.opsForHash().get(key, hashKey);
    }

    /**
     * 从Redis中获取整个哈希表的数据
     *
     * @param key 哈希表的键
     * @param <T> 字段值的类型
     * @return 包含所有字段键值对的映射
     */
    public static <T> Map<String, T> getHash(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获取哈希表中的所有字段键
     *
     * @param key 哈希表的键
     * @return 包含所有字段键的集合
     */
    public static Set<String> getAllHash(String key){
        return redisTemplate.opsForHash().keys(key);
    }

    /**
     * 获取哈希表中的所有字段值
     *
     * @param key 哈希表的键
     * @param <T> 字段值的类型
     * @return 包含所有字段值的列表
     */
    public static <T> List<T> getAllHashValue(String key) {
        return redisTemplate.opsForHash().values(key);
    }

    /**
     * 删除Redis中的哈希表
     *
     * @param key 哈希表的键
     * @return 如果哈希表存在且删除成功返回true，否则返回false
     */
    public static Boolean deleteHash(String key) {
        return redisTemplate.opsForHash().delete(key) > 0;
    }

    /**
     * 删除哈希表中的一个或多个字段
     *
     * @param key 哈希表的键
     * @param hashKeys 要删除的字段键数组
     * @return 如果字段存在且删除成功返回true，否则返回false
     */
    public static Boolean deleteHash(String key, String... hashKeys) {
        return redisTemplate.opsForHash().delete(key, (Object) hashKeys) > 0;
    }

    /**
     * 发送消息到Redis消息队列
     *
     * @param topic Redis消息队列的Topic
     * @param msg 要发送的消息
     * @param <T> 消息的类型
     */
    public static <T> void sendMsg(String topic, T msg) {
        redisTemplate.convertAndSend(topic, JSON.toJSONString(msg));
    }

    /**
     * 根据指定前缀删除键值对
     * @param prefix 键名的前缀
     * @return 如果删除成功则返回true，否则返回false
     */
    public static Boolean deleteByPrefix(String prefix) {
        // 获取所有匹配指定前缀的键名
        List<String> keys = keys(prefix + "*");
        return delete(keys);
    }

    /**
     * 删除使用的Redis缓存。
     * @return true 删除成功 false删除失败
     */
    public static boolean deleteCache() {
        // 获取所有键名
        List<String> keys = keys("*");
        // 删除所有键值对
        return delete(keys);
    }

    /**
     * 设置Redis键的过期时间
     *
     * @param key Redis的键
     * @param time 过期时间，单位为秒
     * @return 返回是否成功设置过期时间
     */
    public static Boolean setExpirationTime(String key, long time){
        return redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    /**
     * 设置Redis键的过期时间
     *
     * @param key Redis的键
     * @param time 过期时间，单位为秒
     * @return 返回是否成功设置过期时间
     */
    public static Boolean setExpirationTime(String key, long time, TimeUnit timeUnit){
        return redisTemplate.expire(key, time, timeUnit);
    }

    /**
     * 获取Redis键的剩余过期时间
     *
     * @param key Redis的键
     * @return 剩余过期时间，单位为秒，如果键不存在或没有设置过期时间，则返回-2；如果键设置了过期时间，则返回剩余过期时间。
     */
    public static Long getExpirationTime(String key){
        return redisTemplate.getExpire(key);
    }

    /**
     * 获取Redis键的剩余过期时间
     *
     * @param key Redis的键
     * @param timeUnit 时间单位
     * @return 剩余过期时间，单位为秒，如果键不存在或没有设置过期时间，则返回-2；如果键设置了过期时间，则返回剩余过期时间。
     */
    public static Long getExpirationTime(String key, TimeUnit timeUnit){
        return redisTemplate.getExpire(key,timeUnit);
    }

    /**
     * 判断缓存中是否有对应的value
     * @return true 存在 false不存在
     */
    public static boolean exists(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

}
